﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using SysImaging = System.Windows.Media.Imaging;
using iNet.Tasks;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 適用於點陣圖的濾鏡工作基底類別。
	/// </summary>
	public abstract class BitmapImageFilterTask : ImageFilterTask
	{
		#region 靜態欄位
		static readonly PixelFormat[] _SupportedPixelFormats = new PixelFormat[]{
			PixelFormats.Bgra32,
			PixelFormats.Bgr24,
			PixelFormats.Bgr32,
			PixelFormats.Rgba64,
			PixelFormats.Rgb48,
		};
		#endregion
		#region 欄位
		Int32Rect _FilterRectangle;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 BitmapImageFilterTask 執行個體。
		/// </summary>
		protected BitmapImageFilterTask()
		{ }
		/// <summary>
		/// 初始化新的 BitmapImageFilterTask 執行個體。
		/// </summary>
		/// <param name="sourceImage">欲套用濾鏡的點陣圖。</param>
		protected BitmapImageFilterTask(IBitmapImage sourceImage)
			: base(sourceImage)
		{ }
		#endregion
		#region ApplyFilter
		/// <summary>
		/// 套用濾鏡。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="sourceImage">欲套用濾鏡的影像。</param>
		/// <param name="resultImage">已套用濾鏡的影像。</param>
		/// <returns>True 表示濾鏡套用成功，False 表示失敗。</returns>
		protected unsafe override bool ApplyFilter(TaskVersion version, IImage sourceImage, out IImage resultImage)
		{
			//初始化參數
			resultImage = null;

			//取得參數
			Int32Rect rect;
			if (!this.ReadValue<Int32Rect>(version, ref _FilterRectangle, out rect))
				return true;

			//取得來源點陣圖
			IBitmapImage bitmap = (sourceImage as IBitmapImage);

			//轉換影像格式
			PixelFormat pixelFormat = bitmap.PixelFormat;
			SysImaging.BitmapPalette palette;
			if (_SupportedPixelFormats.Contains(pixelFormat))
				palette = bitmap.Palette;
			else
			{
				pixelFormat = PixelFormats.Bgra32;
				palette = null;
				bitmap = bitmap.ConvertPixelFormat(pixelFormat, palette);
			}

			//計算套用範圍
			int pixelWidth = bitmap.PixelWidth;
			int pixelHeight = bitmap.PixelHeight;
			if (rect.Width < 0)
				rect.Width = 0;
			if (rect.Height < 0)
				rect.Height = 0;
			if (rect.X < 0)
			{
				rect.Width += rect.X;
				rect.X = 0;
			}
			if (rect.X + rect.Width > pixelWidth)
				rect.Width = (pixelWidth - rect.X);
			if (rect.Y < 0)
			{
				rect.Height += rect.Y;
				rect.Y = 0;
			}
			if (rect.Y + rect.Height > pixelHeight)
				rect.Height = (pixelHeight - rect.Y);

			//直接複製原始影像
			if (rect.X >= pixelWidth || rect.Y >= pixelHeight)
			{
				resultImage = (bitmap.Clone() as IImage);
				return true;
			}

			//複製來源像素資料
			int filterPixelWidth;
			int filterPixelHeight;
			if (rect.Width == 0 || rect.Height == 0)
			{
				filterPixelWidth = pixelWidth;
				filterPixelHeight = pixelHeight;
				rect.Width = 0;
				rect.Height = 0;
			}
			else
			{
				filterPixelWidth = rect.Width;
				filterPixelHeight = rect.Height;
			}
			int stride = (filterPixelWidth * pixelFormat.BitsPerPixel / 8);
			byte[] srcPixelData = bitmap.CopyPixelsToByteArray(rect, pixelFormat, palette);
			
			//建立目標點陣圖
			SysImaging.WriteableBitmap resultBitmap;
			if (bitmap.PixelFormat == pixelFormat && bitmap.Palette == palette)
				resultBitmap = new SysImaging.WriteableBitmap(bitmap.ToImageSource().GetAsBitmapSource());
			else
				resultBitmap = new SysImaging.WriteableBitmap(new SysImaging.FormatConvertedBitmap(bitmap.ToImageSource().GetAsBitmapSource(), pixelFormat, palette, 0.5));

			//中斷確認
			if (this.IsCanceling)
				return true;

			//套用濾鏡
			fixed (byte* srcPixelBuffer = srcPixelData)
			{
				byte[] resultPixelData = new byte[srcPixelData.Length];
				resultBitmap.CopyPixels(rect, resultPixelData, stride, 0);
				fixed (byte* resultPixelBuffer = resultPixelData)
				{
					BitmapImageFilterData filterData = new BitmapImageFilterData(filterPixelWidth, filterPixelHeight, stride, pixelFormat, palette, new IntPtr(srcPixelBuffer), new IntPtr(resultPixelBuffer));
					if (!this.ApplyFilter(version, filterData))
						return false;

				}
				if (rect.IsEmpty)
					rect = new Int32Rect(0, 0, pixelWidth, pixelHeight);
				resultBitmap.WritePixels(rect, resultPixelData, stride, 0);
			}
			resultBitmap.Freeze();

			//中斷確認
			if (this.IsCanceling)
				return true;

			//轉成 IBitmapImage 介面
			resultImage = new BitmapImage(sourceImage.MediaStorage, resultBitmap, true, sourceImage.Metadata, sourceImage.RawFormat);

			//完成
			return true;
		}
		/// <summary>
		/// 套用濾鏡至點陣圖。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="filterData">套用濾鏡所需的資料。</param>
		/// <returns>True 表示濾鏡套用成功，False 表示失敗。</returns>
		protected virtual bool ApplyFilter(TaskVersion version, BitmapImageFilterData filterData)
		{
			return true;
		}
		#endregion
		#region EnumerateSourceBitmapArgb
		/// <summary>
		/// 列舉欲套用濾鏡之來源陣圖中每個像素的 ARGB。
		/// </summary>
		/// <param name="filterData">濾鏡套用資料。</param>
		/// <param name="callback">列舉 ARGB 時所回呼的方法。</param>
		/// <returns>True 表示列舉完成，False 表示中止列舉。</returns>
		/// <exception cref="ArgumentNullException">filterData 或 callback 為 Null (Visual Basic 為 Nothing)。</exception>
		protected bool EnumerateSourceBitmapArgb(BitmapImageFilterData filterData, EnumerateArgbCallback callback)
		{
			//確認參數
			if (filterData == null)
				throw new ArgumentNullException("filterData");
			if (callback == null)
				throw new ArgumentNullException("callback");

			//列舉像素資料
			return ImageUtility.EnumerateArgb(filterData.SourcePixelBuffer, filterData.PixelFormat, filterData.Palette, filterData.PixelWidth, filterData.PixelHeight, filterData.Stride, callback);
		}
		#endregion
		#region FilterRectangle
		/// <summary>
		/// 取得或設定套用濾鏡的矩形範圍，單位為像素。Int32Rect.Empty 表示套用至整張影像。
		/// </summary>
		public Int32Rect FilterRectangle
		{
			get
			{
				return _FilterRectangle;
			}
			set
			{
				this.SetInitializingParameter<Int32Rect>(ref _FilterRectangle, value);
			}
		}
		#endregion
		#region SourceImage
		/// <summary>
		/// 取得或設定欲套用濾鏡的點陣圖。
		/// </summary>
		public override IImage SourceImage
		{
			get
			{
				return base.SourceImage;
			}
			set
			{
				//確認參數
				if (value != null && !(value is IBitmapImage))
					throw new ArgumentException();

				//設定參數
				base.SourceImage = value;
			}
		}
		#endregion
	}
	#region BitmapImageFilterChannels
	/// <summary>
	/// 表示點陣圖濾鏡的目標色彩組合。
	/// </summary>
	[Flags]
	public enum BitmapImageFilterChannels
	{
		/// <summary>
		/// 光線強度。
		/// </summary>
		Luminance = 0x1,
		/// <summary>
		/// 透明度。
		/// </summary>
		Alpha = 0x2,
		/// <summary>
		/// 紅色。
		/// </summary>
		Red = 0x10,
		/// <summary>
		/// 綠色。
		/// </summary>
		Green = 0x20,
		/// <summary>
		/// 藍色。
		/// </summary>
		Blue = 0x40,
		/// <summary>
		/// 紅色、綠色及藍色。
		/// </summary>
		Rgb = 0x70,
		/// <summary>
		/// 透明度、紅色、綠色及藍色。
		/// </summary>
		Argb = 0x72,
	}
	#endregion
	#region BitmapImageFilterData
	/// <summary>
	/// 提供點陣圖濾鏡運算的相關資訊。
	/// </summary>
	public unsafe class BitmapImageFilterData
	{
		#region 欄位
		readonly SysImaging.BitmapPalette _Palette;
		readonly PixelFormat _PixelFormat;
		readonly int _PixelHeight;
		readonly int _PixelWidth;
		readonly IntPtr _ResultPixelBuffer;
		readonly IntPtr _SourcePixelBuffer;
		readonly int _Stride;
        #endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 BitmapImageFilterData 執行個體。
		/// </summary>
		/// <param name="pixelWidth">像素寬度。</param>
		/// <param name="pixelHeight">像素高度。</param>
		/// <param name="stride">掃描線的寬度，或稱為步距，單位為位元組。</param>
		/// <param name="pixelFormat">像素格式。</param>
		/// <param name="srcPixelBuffer">來源點陣圖像素資料。</param>
		/// <param name="resultPixelBuffer">儲存運算結果的點陣圖像素資料。</param>
		/// <exception cref="ArgumentOutOfRangeException">pixelWidth、pixelHeight 或 stride 小於等於零。</exception>
		public BitmapImageFilterData(int pixelWidth, int pixelHeight, int stride, PixelFormat pixelFormat, IntPtr srcPixelBuffer, IntPtr resultPixelBuffer)
			: this(pixelWidth, pixelHeight, stride, pixelFormat, null, srcPixelBuffer, resultPixelBuffer)
		{ }
		/// <summary>
		/// 初始化新的 BitmapImageFilterData 執行個體。
		/// </summary>
		/// <param name="pixelWidth">像素寬度。</param>
		/// <param name="pixelHeight">像素高度。</param>
		/// <param name="stride">掃描線的寬度，或稱為步距，單位為位元組。</param>
		/// <param name="pixelFormat">像素格式。</param>
		/// <param name="palette">調色盤。</param>
		/// <param name="srcPixelBuffer">來源點陣圖像素資料。</param>
		/// <param name="resultPixelBuffer">儲存運算結果的點陣圖像素資料。</param>
		/// <exception cref="ArgumentOutOfRangeException">pixelWidth、pixelHeight 或 stride 小於等於零。</exception>
		public BitmapImageFilterData(int pixelWidth, int pixelHeight, int stride, PixelFormat pixelFormat, SysImaging.BitmapPalette palette, IntPtr srcPixelBuffer, IntPtr resultPixelBuffer)
		{
			if (pixelWidth <= 0 || pixelHeight <= 0 || stride <= 0)
				throw new ArgumentOutOfRangeException();
			_PixelWidth = pixelWidth;
			_PixelHeight = pixelHeight;
			_Stride = stride;
			_PixelFormat = pixelFormat;
			_Palette = palette;
			_SourcePixelBuffer = srcPixelBuffer;
			_ResultPixelBuffer = resultPixelBuffer;
		}
        #endregion
		#region BitsPerPixel
		/// <summary>
		/// 取得單一像素所使用的位元數 (bpp)。
		/// </summary>
		public int BitsPerPixel
		{
			get
			{
				return _PixelFormat.BitsPerPixel;
			}
		}
        #endregion
		#region BytesPerPixel
		/// <summary>
		/// 取得單一像素所使用的位元組數。
		/// </summary>
		public int BytesPerPixel
		{
			get
			{
				return (_PixelFormat.BitsPerPixel / 8);
			}
		}
		#endregion
		#region GetPixelBufferAs2DByteArray
		byte*[] GetPixelBufferAs2DByteArray(IntPtr pixelBuffer)
		{
			byte*[] array = new byte*[_PixelHeight];
			byte* basePtr = (byte*)pixelBuffer;
			for (int i = 0; i < _PixelHeight; ++i, basePtr += _Stride)
				array[i] = basePtr;
			return array;
		}
		#endregion
		#region GetResultPixelBufferAs2DByteArray
		/// <summary>
		/// 以二維位元組陣列形式取得儲存運算結果的點陣圖像素資料。
		/// </summary>
		/// <returns>儲存運算結果的點陣圖像素資料。</returns>
		public byte*[] GetResultPixelBufferAs2DByteArray()
		{
			return this.GetPixelBufferAs2DByteArray(_ResultPixelBuffer);
		}
		#endregion
		#region GetSourcePixelBufferAs2DByteArray
		/// <summary>
		/// 以二維位元組陣列形式取得來源點陣圖像素資料。
		/// </summary>
		/// <returns>來源點陣圖像素資料。</returns>
		public byte*[] GetSourcePixelBufferAs2DByteArray()
		{
			return this.GetPixelBufferAs2DByteArray(_SourcePixelBuffer);
		}
        #endregion
		#region Is16BitsColor
		/// <summary>
		/// 取得值以確認是否使用 16 位元表示單一色彩。
		/// </summary>
		public bool Is16BitsColor
		{
			get
			{
				return (_PixelFormat == PixelFormats.Rgb48 || _PixelFormat == PixelFormats.Rgba64);
			}
		}
        #endregion
		#region IsAlphaIncluded
		/// <summary>
		/// 取得值以確認像素資料中是否包含 Alpha 值。
		/// </summary>
		public bool IsAlphaIncluded
		{
			get
			{
				return (_PixelFormat == PixelFormats.Bgra32
					|| _PixelFormat == PixelFormats.Rgba64
					|| _PixelFormat == PixelFormats.Pbgra32
					|| _PixelFormat == PixelFormats.Prgba128Float
					|| _PixelFormat == PixelFormats.Prgba64
					|| _PixelFormat == PixelFormats.Rgba128Float);
			}
		}
        #endregion
		#region Palette
		/// <summary>
		/// 取得調色盤。
		/// </summary>
		public SysImaging.BitmapPalette Palette
		{
			get
			{
				return _Palette;
			}
		}
		#endregion
		#region PixelFormat
		/// <summary>
		/// 取得像素格式。
		/// </summary>
		public PixelFormat PixelFormat
		{
			get
			{
				return _PixelFormat;
			}
		}
		#endregion
		#region PixelHeight
		/// <summary>
		/// 取得像素高度。
		/// </summary>
		public int PixelHeight
		{
			get
			{
				return _PixelHeight;
			}
		}
		#endregion
		#region PixelWidth
		/// <summary>
		/// 取得像素寬度。
		/// </summary>
		public int PixelWidth
		{
			get
			{
				return _PixelWidth;
			}
		}
		#endregion
		#region ResultPixelBuffer
		/// <summary>
		/// 取得來源點陣圖像素資料。
		/// </summary>
		public IntPtr ResultPixelBuffer
		{
			get
			{
				return _ResultPixelBuffer;
			}
		}
		#endregion
		#region SourcePixelBuffer
		/// <summary>
		/// 取得來源點陣圖像素資料。
		/// </summary>
		public IntPtr SourcePixelBuffer
		{
			get
			{
				return _SourcePixelBuffer;
			}
		}
		#endregion
		#region Stride
		/// <summary>
		/// 取得掃描線的寬度，或稱為步距，單位為位元組。
		/// </summary>
		public int Stride
		{
			get
			{
				return _Stride;
			}
		}
		#endregion
	}
	#endregion
}
